package com.yugao.fintech.libra.workflow.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.yugao.fintech.meheco.core.builder.RemoteCallBuilder;
import com.yugao.fintech.meheco.core.constant.SecurityConstants;
import com.yugao.fintech.meheco.core.constant.ServiceIdConstants;
import com.yugao.fintech.meheco.core.exception.FlowException;
import com.yugao.fintech.meheco.core.utils.CollectionUtil;
import com.yugao.fintech.draper.security.utils.SecurityUtils;
import com.yugao.fintech.libra.auth.api.model.SysRole;
import com.yugao.fintech.libra.auth.api.model.SysUser;
import com.yugao.fintech.libra.auth.api.service.RoleFeignApi;
import com.yugao.fintech.libra.auth.api.service.UserFeignApi;
import com.yugao.fintech.libra.workflow.constant.FlowComment;
import com.yugao.fintech.libra.workflow.constant.ProcessConstants;
import com.yugao.fintech.libra.workflow.domain.FlowTaskRelation;
import com.yugao.fintech.libra.workflow.domain.dto.*;
import com.yugao.fintech.libra.workflow.domain.vo.FlowTaskVo;
import com.yugao.fintech.libra.workflow.service.AbstractFlowService;
import com.yugao.fintech.libra.workflow.service.IFlowRelationService;
import com.yugao.fintech.libra.workflow.service.IFlowTaskService;
import com.yugao.fintech.libra.workflow.util.CustomProcessDiagramGenerator;
import com.yugao.fintech.libra.workflow.util.FindNextNodeUtil;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.EndEvent;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.identitylink.api.history.HistoricIdentityLink;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.InputStream;
import java.util.*;

@Service
public class FlowTaskServiceImpl extends AbstractFlowService implements IFlowTaskService {

    @Resource
    private UserFeignApi userFeignApi;

    @Resource
    private RoleFeignApi roleFeignApi;

    @Autowired
    private IFlowRelationService flowRelationService;

    /**
     * 完成任务
     *
     * @param taskVo 请求实体参数
     */
    @Transactional
    @Override
    public Boolean complete(FlowTaskVo taskVo) {
        Boolean finished = false;
        Task task = taskService.createTaskQuery().taskId(taskVo.getTaskId()).singleResult();
        if (Objects.isNull(task)) {
            throw new FlowException("任务不存在");
        }
        if (DelegationState.PENDING.equals(task.getDelegationState())) {
            taskService.addComment(taskVo.getTaskId(), taskVo.getInstanceId(), FlowComment.DELEGATE.getType(), taskVo.getComment());
            taskService.resolveTask(taskVo.getTaskId(), taskVo.getValues());
        } else {
            taskService.addComment(taskVo.getTaskId(), taskVo.getInstanceId(), FlowComment.NORMAL.getType(), taskVo.getComment());
            Long userId = SecurityUtils.getLoginUser().getSysUser().getUserId();
            taskService.setAssignee(taskVo.getTaskId(), userId.toString());
            taskService.complete(taskVo.getTaskId(), taskVo.getValues());
        }
        // 查询当前任务是否完成
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(taskVo.getInstanceId()).list();
        if (CollectionUtil.isEmpty(taskList)) {
            finished = true;
        }
        return finished;
    }

    /**
     * 删除任务
     *
     * @param flowTaskVo 请求实体参数
     */
    @Transactional
    @Override
    public void deleteTask(FlowTaskVo flowTaskVo) {
        taskService.deleteTask(flowTaskVo.getTaskId(), flowTaskVo.getComment());
    }

    /**
     * 取消申请
     *
     * @param flowTaskVo
     * @return
     */
    @Transactional
    @Override
    public void stopProcess(FlowTaskVo flowTaskVo) {
        List<Task> task = taskService.createTaskQuery().processInstanceId(flowTaskVo.getInstanceId()).list();
        if (CollectionUtil.isEmpty(task)) {
            throw new FlowException("流程已取消或已完成，取消申请失败");
        }
        // 获取当前需撤回的流程实例
        ProcessInstance processInstance =
                runtimeService.createProcessInstanceQuery()
                        .processInstanceId(flowTaskVo.getInstanceId())
                        .singleResult();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
        if (Objects.nonNull(bpmnModel)) {
            Process process = bpmnModel.getMainProcess();
            List<EndEvent> endNodes = process.findFlowElementsOfType(EndEvent.class, false);
            if (CollectionUtil.isNotEmpty(endNodes)) {
                SysUser loginUser = SecurityUtils.getLoginUser().getSysUser();
                Authentication.setAuthenticatedUserId(loginUser.getUserId().toString());
                // 获取当前流程最后一个节点
                String endId = endNodes.get(0).getId();
                List<Execution> executions =
                        runtimeService.createExecutionQuery().parentId(processInstance.getProcessInstanceId()).list();
                List<String> executionIds = new ArrayList<>();
                executions.forEach(execution -> executionIds.add(execution.getId()));
                // 变更流程为已结束状态
                runtimeService.createChangeActivityStateBuilder()
                        .moveExecutionsToSingleActivityId(executionIds, endId).changeState();
            }
        }
    }


    /**
     * 代办任务列表
     *
     * @param pageNum  当前页码
     * @param pageSize 每页条数
     * @return
     */
    @Override
    public Page<FlowTaskDto> todoList(Integer pageNum, Integer pageSize) {
        Page<FlowTaskDto> page = new Page<>();
        SysUser user = SecurityUtils.getLoginUser().getSysUser();
        TaskQuery taskQuery = taskService.createTaskQuery()
                .active()
                .includeProcessVariables()
                .orderByTaskCreateTime().desc();
        if (!SecurityConstants.SUPER_ADMIN.equals(user.getUserName())) {
            taskQuery.taskAssignee(user.getUserId().toString());
        }
        page.setTotal(taskQuery.count());
        List<Task> taskList = taskQuery.listPage(pageSize * (pageNum - 1), pageSize);
        List<FlowTaskDto> flowList = new ArrayList<>();
        for (Task task : taskList) {
            FlowTaskDto flowTask = new FlowTaskDto();
            // 当前流程信息
            flowTask.setTaskId(task.getId());
            flowTask.setTaskDefKey(task.getTaskDefinitionKey());
            flowTask.setCreateTime(task.getCreateTime());
            flowTask.setProcDefId(task.getProcessDefinitionId());
            flowTask.setExecutionId(task.getExecutionId());
            flowTask.setTaskName(task.getName());
            flowTask.setProcInsId(task.getProcessInstanceId());

            FlowProcDefDto flowProcDef = flowRelationService.queryDefByProcDefId(task.getProcessDefinitionId());
            flowTask.setDeployId(flowProcDef.getDeploymentId());
            flowTask.setProcDefName(flowProcDef.getName());
            flowTask.setProcDefVersion(flowProcDef.getVersion());
            flowTask.setCategory(flowProcDef.getCategory());

            fillTask(flowTask);

            flowList.add(flowTask);
        }

        page.setRecords(flowList);
        return page;
    }


    /**
     * 已办任务列表
     *
     * @param pageNum  当前页码
     * @param pageSize 每页条数
     * @return
     */
    @Override
    public Page<FlowTaskDto> finishedList(Integer pageNum, Integer pageSize) {
        Page<FlowTaskDto> page = new Page<>();
        SysUser user = SecurityUtils.getLoginUser().getSysUser();
        HistoricTaskInstanceQuery taskInstanceQuery = historyService.createHistoricTaskInstanceQuery()
                .includeProcessVariables()
                .finished()
                .orderByHistoricTaskInstanceEndTime()
                .desc();
        if (!SecurityConstants.SUPER_ADMIN.equals(user.getUserName())) {
            taskInstanceQuery.taskAssignee(user.getUserId().toString());
        }
        List<HistoricTaskInstance> historicTaskInstanceList = taskInstanceQuery.listPage(pageSize * (pageNum - 1), pageSize);
        List<FlowTaskDto> hisTaskList = Lists.newArrayList();
        for (HistoricTaskInstance histTask : historicTaskInstanceList) {
            FlowTaskDto flowTask = new FlowTaskDto();
            // 当前流程信息
            flowTask.setTaskId(histTask.getId());
            // 审批人员信息
            flowTask.setCreateTime(histTask.getCreateTime());
            flowTask.setFinishTime(histTask.getEndTime());
            flowTask.setDuration(getDate(histTask.getDurationInMillis()));
            flowTask.setProcDefId(histTask.getProcessDefinitionId());
            flowTask.setTaskDefKey(histTask.getTaskDefinitionKey());
            flowTask.setTaskName(histTask.getName());
            flowTask.setExecutionId(histTask.getExecutionId());
            flowTask.setProcInsId(histTask.getProcessInstanceId());

            FlowProcDefDto flowProcDef = flowRelationService.queryDefByProcDefId(histTask.getProcessDefinitionId());
            flowTask.setDeployId(flowProcDef.getDeploymentId());
            flowTask.setProcDefName(flowProcDef.getName());
            flowTask.setProcDefVersion(flowProcDef.getVersion());
            flowTask.setCategory(flowProcDef.getCategory());

            fillTask(flowTask);

            hisTaskList.add(flowTask);
        }
        page.setTotal(taskInstanceQuery.count());
        page.setRecords(hisTaskList);
        return page;
    }

    private void fillTask(FlowTaskDto flowTask) {
        FlowTaskRelation flowTaskRelation = flowRelationService.queryTaskByProcInsId(flowTask.getProcInsId());
        flowTask.setRefId(flowTaskRelation.getRefId());
        flowTask.setStatus(flowTaskRelation.getStatus());
        flowTask.setFlowCode(flowTaskRelation.getFlowCode());
        flowTask.setFlowName(flowTaskRelation.getFlowName());
    }

    /**
     * 流程历史流转记录
     *
     * @param procInsId 流程实例Id
     * @return
     */
    @Override
    public Map<String, Object> flowRecord(String procInsId, String deployId) {
        Map<String, Object> map = new HashMap<String, Object>();
        if (StringUtils.isNotBlank(procInsId)) {
            List<HistoricActivityInstance> list = historyService
                    .createHistoricActivityInstanceQuery()
                    .processInstanceId(procInsId)
                    .orderByHistoricActivityInstanceStartTime()
                    .desc().list();
            List<FlowTaskDto> hisFlowList = new ArrayList<>();
            for (HistoricActivityInstance histIns : list) {
                if (StringUtils.isNotBlank(histIns.getTaskId())) {
                    FlowTaskDto flowTask = new FlowTaskDto();
                    flowTask.setTaskId(histIns.getTaskId());
                    flowTask.setTaskName(histIns.getActivityName());
                    flowTask.setCreateTime(histIns.getStartTime());
                    flowTask.setFinishTime(histIns.getEndTime());
                    if (StringUtils.isNotBlank(histIns.getAssignee())) {
                        SysUser sysUser = RemoteCallBuilder.build(ServiceIdConstants.WORKFLOW_SERVICE, "FlowTaskServiceImpl", "flowRecord", Collections.singletonMap("id", Long.parseLong(histIns.getAssignee()))).call(
                                () -> userFeignApi.getUserInfoById(Long.parseLong(histIns.getAssignee()), SecurityConstants.INNER));
                        if (Objects.nonNull(sysUser)) {
                            flowTask.setAssigneeId(sysUser.getUserId());
                            flowTask.setAssigneeName(sysUser.getNickName());
                            if (Objects.nonNull(sysUser.getDept())) {
                                flowTask.setDeptName(sysUser.getDept().getDeptName());
                            }
                        } else {
                            throw new FlowException("审核人员不存在");
                        }
                    }
                    // 展示审批人员
                    List<HistoricIdentityLink> linksForTask = historyService.getHistoricIdentityLinksForTask(histIns.getTaskId());
                    StringBuilder stringBuilder = new StringBuilder();
                    for (HistoricIdentityLink identityLink : linksForTask) {
                        // 获选人,候选组/角色(多个)
                        if ("candidate".equals(identityLink.getType())) {
                            if (StringUtils.isNotBlank(identityLink.getUserId())) {
                                SysUser sysUser = RemoteCallBuilder.build(ServiceIdConstants.WORKFLOW_SERVICE, "FlowTaskServiceImpl", "flowRecord", Collections.singletonMap("id", Long.parseLong(identityLink.getUserId()))).call(
                                        () -> userFeignApi.getUserInfoById(Long.parseLong(identityLink.getUserId()), SecurityConstants.INNER));
                                stringBuilder.append(sysUser.getNickName()).append(",");
                            }
                            if (StringUtils.isNotBlank(identityLink.getGroupId())) {
                                SysRole sysRole = RemoteCallBuilder.build(ServiceIdConstants.WORKFLOW_SERVICE, "FlowTaskServiceImpl", "flowRecord", Collections.singletonMap("id", Long.parseLong(identityLink.getGroupId()))).call(
                                        () -> roleFeignApi.getRoleInfoById(Long.parseLong(identityLink.getGroupId()), SecurityConstants.INNER));
                                stringBuilder.append(sysRole.getRoleName()).append(",");
                            }
                        }
                    }
                    if (StringUtils.isNotBlank(stringBuilder)) {
                        flowTask.setCandidate(stringBuilder.substring(0, stringBuilder.length() - 1));
                    }

                    flowTask.setDuration(histIns.getDurationInMillis() == null || histIns.getDurationInMillis() == 0 ? null : getDate(histIns.getDurationInMillis()));
                    // 获取意见评论内容
                    List<Comment> commentList = taskService.getProcessInstanceComments(histIns.getProcessInstanceId());
                    commentList.forEach(comment -> {
                        if (histIns.getTaskId().equals(comment.getTaskId())) {
                            flowTask.setComment(FlowCommentDto.builder().type(comment.getType()).comment(comment.getFullMessage()).build());
                        }
                    });
                    hisFlowList.add(flowTask);
                }
            }
            map.put("flowList", hisFlowList);
        }
        return map;
    }

    /**
     * 获取流程过程图
     *
     * @param processId
     * @return
     */
    @Override
    public InputStream diagram(String processId) {
        String processDefinitionId;
        // 获取当前的流程实例
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
        // 如果流程已经结束，则得到结束节点
        if (Objects.isNull(processInstance)) {
            HistoricProcessInstance pi = historyService.createHistoricProcessInstanceQuery().processInstanceId(processId).singleResult();

            processDefinitionId = pi.getProcessDefinitionId();
        } else {// 如果流程没有结束，则取当前活动节点
            // 根据流程实例ID获得当前处于活动状态的ActivityId合集
            ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
            processDefinitionId = pi.getProcessDefinitionId();
        }

        // 获得活动的节点
        List<HistoricActivityInstance> highLightedFlowList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processId).orderByHistoricActivityInstanceStartTime().asc().list();

        List<String> highLightedFlows = new ArrayList<>();
        List<String> highLightedNodes = new ArrayList<>();
        //高亮线
        for (HistoricActivityInstance tempActivity : highLightedFlowList) {
            if ("sequenceFlow".equals(tempActivity.getActivityType())) {
                //高亮线
                highLightedFlows.add(tempActivity.getActivityId());
            } else {
                //高亮节点
                highLightedNodes.add(tempActivity.getActivityId());
            }
        }

        //获取流程图
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        ProcessEngineConfiguration configuration = processEngine.getProcessEngineConfiguration();
        //获取自定义图片生成器
        ProcessDiagramGenerator diagramGenerator = new CustomProcessDiagramGenerator();
        InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", highLightedNodes, highLightedFlows, configuration.getActivityFontName(),
                configuration.getLabelFontName(), configuration.getAnnotationFontName(), configuration.getClassLoader(), 1.0, true);
        return in;

    }

    /**
     * 获取流程执行过程
     *
     * @param procInsId 流程实例id
     * @return
     */
    @Override
    public List<FlowViewerDto> getFlowViewer(String procInsId, String executionId) {
        List<FlowViewerDto> flowViewerList = new ArrayList<>();
        FlowViewerDto flowViewerDto;
        // 获取任务开始节点(临时处理方式)
        List<HistoricActivityInstance> startNodeList = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(procInsId)
                .orderByHistoricActivityInstanceStartTime()
                .asc().listPage(0, 3);
        for (HistoricActivityInstance startInstance : startNodeList) {
            if (!"sequenceFlow".equals(startInstance.getActivityType())) {
                flowViewerDto = new FlowViewerDto();
                if (!"sequenceFlow".equals(startInstance.getActivityType())) {
                    flowViewerDto.setKey(startInstance.getActivityId());
                    // 根据流程节点处理时间校验改节点是否已完成
                    flowViewerDto.setCompleted(!Objects.isNull(startInstance.getEndTime()));
                    flowViewerList.add(flowViewerDto);
                }
            }
        }
        // 历史节点
        List<HistoricActivityInstance> hisActIns = historyService.createHistoricActivityInstanceQuery()
                .executionId(executionId)
                .orderByHistoricActivityInstanceStartTime()
                .asc().list();
        for (HistoricActivityInstance activityInstance : hisActIns) {
            if (!"sequenceFlow".equals(activityInstance.getActivityType())) {
                flowViewerDto = new FlowViewerDto();
                flowViewerDto.setKey(activityInstance.getActivityId());
                // 根据流程节点处理时间校验改节点是否已完成
                flowViewerDto.setCompleted(!Objects.isNull(activityInstance.getEndTime()));
                flowViewerList.add(flowViewerDto);
            }
        }
        return flowViewerList;
    }

    /**
     * 获取流程变量
     *
     * @param taskId
     * @return
     */
    @Override
    public Map<String, Object> processVariables(String taskId) {
        // 流程变量
        Map<String, Object> variables;
        HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().includeProcessVariables().finished().taskId(taskId).singleResult();
        if (Objects.nonNull(historicTaskInstance)) {
            variables = historicTaskInstance.getProcessVariables();
        } else {
            variables = taskService.getVariables(taskId);
        }
        return variables;
    }

    /**
     * 获取下一节点
     *
     * @param flowTaskVo 任务
     * @return
     */
    @Override
    public FlowNextDto getNextFlowNode(FlowTaskVo flowTaskVo) {
        // Step 1. 获取当前节点并找到下一步节点
        Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult();
        FlowNextDto flowNextDto = null;
        if (Objects.nonNull(task)) {
            // Step 2. 获取当前流程所有流程变量(网关节点时需要校验表达式)
            Map<String, Object> variables = taskService.getVariables(task.getId());
            List<UserTask> nextUserTask = FindNextNodeUtil.getNextUserTasks(repositoryService, task, variables);
            if (CollectionUtil.isNotEmpty(nextUserTask)) {
                flowNextDto = new FlowNextDto();
                for (UserTask userTask : nextUserTask) {
                    // 目前只支持 指定个人 审核
                    flowNextDto.setType(ProcessConstants.DATA_TYPE_FIXED);
                }
            }
        }
        return flowNextDto;
    }

    /**
     * 流程完成时间处理
     *
     * @param ms
     * @return
     */
    private String getDate(long ms) {

        long day = ms / (24 * 60 * 60 * 1000);
        long hour = (ms / (60 * 60 * 1000) - day * 24);
        long minute = ((ms / (60 * 1000)) - day * 24 * 60 - hour * 60);
        long second = (ms / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60);

        if (day > 0) {
            return day + "天" + hour + "小时" + minute + "分钟";
        }
        if (hour > 0) {
            return hour + "小时" + minute + "分钟";
        }
        if (minute > 0) {
            return minute + "分钟";
        }
        if (second > 0) {
            return second + "秒";
        } else {
            return 0 + "秒";
        }
    }
}